package gsp

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"reflect"

	V "gitee.com/freelifer/go-spring-plus/gsp/validator"
	"gitee.com/freelifer/go-spring-plus/request"
	"github.com/go-playground/validator/v10"
	"github.com/go-spring/spring-base/util"
	"github.com/go-spring/spring-core/web"
)

const (
	// 格式错误
	DATA_FORMAT_INVALID = 40000

	// 数据不存在
	DATA_NOT_FOUND = 40001

	// 数据已存在
	DATA_EXISTED = 40002

	// 数据无效
	DATA_INVALID = 40003

	// 登录失败
	LOGIN_FAILURE = 50001
)

// RestError 定义 RPC 返回值
type RestError struct {
	Status int         `json:"-"`                 // 错误http status code
	Code   int         `json:"code,omitempty"`    // 业务错误 code
	Err    string      `json:"message,omitempty"` // 错误源
	Data   interface{} `json:"data,omitempty"`    // 返回值
}

func (r *RestError) Error() string {
	return r.Err
}

func PanicWithBadCredentials() *util.PanicCond {
	return util.NewPanicCond(func() interface{} { return NewRestError(http.StatusUnauthorized, "bad credentials") })
}
func PanicWithBadRequest() *util.PanicCond {
	return util.NewPanicCond(func() interface{} { return NewRestError(http.StatusBadRequest, "bad request") })
}
func PanicWithBadRequestAndString(errString string) *util.PanicCond {
	return util.NewPanicCond(func() interface{} { return NewRestError(http.StatusBadRequest, errString) })
}
func PanicWithBadRequestAndError(err error) *util.PanicCond {
	return util.NewPanicCond(func() interface{} { return NewRestError(http.StatusBadRequest, err.Error()) })
}

// NewRest400Error 客户端请求参数不匹配
func NewRest500Error(code int, err string) *RestError {
	return &RestError{Status: http.StatusInternalServerError, Code: code, Err: err}
}

// NewRest400Error 客户端请求参数不匹配
func NewRest400Error(code int, err string) *RestError {
	return &RestError{Status: http.StatusBadRequest, Code: code, Err: err}
}

func NewRestError(status int, err string) *RestError {
	return &RestError{Status: status, Err: err}
}

// contextType context.Context 的反射类型
var contextType = reflect.TypeOf((*web.Context)(nil)).Elem()

// requestQueryParamPtrType request.QueryParam 的反射类型
var requestQueryParamPtrType = reflect.TypeOf((*request.QueryParam)(nil))

// requestPathParamPtrType request.PathParam 的反射类型
var requestPathParamPtrType = reflect.TypeOf((*request.PathParam)(nil))

// requestTokenParamPtrType request.TokenParam 的反射类型
var requestTokenParamPtrType = reflect.TypeOf((*request.TokenParam)(nil))

type restHandleDefaultValue struct {
	Field reflect.StructField
	Index int
	Val   string
}

// restHandler BIND 形式的 Web 处理接口
type restHandler struct {
	fn                    interface{}
	fnType                reflect.Type
	fnValue               reflect.Value
	hasContext            bool
	pathParamType         reflect.Type
	queryParamType        reflect.Type
	tokenParamType        reflect.Type
	bindType              reflect.Type
	restBindDefaultValues []*restHandleDefaultValue
}

func (b *restHandler) Invoke(ctx web.Context) {
	RpcInvoke(ctx, b.call)
}

// FileLine 获取用户函数的文件名、行号以及函数名称
func (b *restHandler) FileLine() (file string, line int, fnName string) {
	return util.FileLine(b.fn)
}

func (b *restHandler) call(ctx web.Context) interface{} {
	// 执行处理函数，并返回结果
	var in []reflect.Value

	// context 上下文
	if b.hasContext {
		in = append(in, reflect.ValueOf(ctx))
	}
	// TokenParam
	if b.tokenParamType != nil {
		valueType := b.tokenParamType.Elem()
		pathParamVal := reflect.New(valueType)
		for i := 0; i < valueType.NumField(); i++ {
			field := valueType.Field(i)

			// 判断是否设置了这个tag
			formTag := field.Tag.Get("form")
			if formTag == "" || formTag == "-" {
				continue
			}
			in := ctx.Get(formTag)
			v := Strval(in)
			value := pathParamVal.Elem().Field(i)
			err := mapping(field, value, v)
			if err != nil {
				panic(err)
			}
		}

		in = append(in, pathParamVal)
	}

	// PathParam
	if b.pathParamType != nil {
		valueType := b.pathParamType.Elem()
		pathParamVal := reflect.New(valueType)
		pathParams := arrayToMap(ctx.PathParamNames(), ctx.PathParamValues())
		// fmt.Println("xxx", pathParams)
		for i := 0; i < valueType.NumField(); i++ {
			field := valueType.Field(i)

			// 判断是否设置了这个tag
			formTag := field.Tag.Get("form")
			if formTag == "" || formTag == "-" {
				continue
			}
			v := pathParams[formTag]
			value := pathParamVal.Elem().Field(i)
			err := mapping(field, value, v)
			if err != nil {
				panic(err)
			}
		}

		in = append(in, pathParamVal)
	}

	// QueryParam
	if b.queryParamType != nil {
		valueType := b.queryParamType.Elem()
		queryParamVal := reflect.New(valueType)
		queryParams := ctx.QueryParams()
		// fmt.Println("xxx", queryParams)
		for i := 0; i < valueType.NumField(); i++ {
			field := valueType.Field(i)

			// 判断是否设置了这个tag
			formTag := field.Tag.Get("form")
			if formTag == "" || formTag == "-" {
				continue
			}
			v := queryParams[formTag]
			if v != nil {
				value := queryParamVal.Elem().Field(i)
				err := mapping(field, value, v[0])
				if err != nil {
					panic(err)
				}
			}
		}

		in = append(in, queryParamVal)
	}

	// 反射创建需要绑定请求参数
	if b.bindType != nil {
		bindVal := reflect.New(b.bindType.Elem())
		// fmt.Println("xxx", b.bindType, bindVal, ctx.QueryParams())
		err := ctx.Bind(bindVal.Interface())
		// fmt.Println("xxx", err)
		util.NewPanicCond(func() interface{} {
			verr, ok := err.(validator.ValidationErrors)
			if !ok {
				// json解析异常
				return NewRest400Error(DATA_FORMAT_INVALID, err.Error())
			}
			result := ""
			for _, split := range verr {
				result = split.Translate(V.Trans)
			}
			if result == "" {
				result = "unknown"
			}

			// 参数校验异常
			return NewRest400Error(DATA_INVALID, result)
		}).When(err != nil)

		// 设置默认值
		doDefaultValue(b.restBindDefaultValues, bindVal.Elem())
		in = append(in, bindVal)
	}

	return b.fnValue.Call(in)[0].Interface()
}

func doDefaultValue(values []*restHandleDefaultValue, value reflect.Value) {
	for _, item := range values {
		switch fieldType := item.Field.Type.Kind(); fieldType {
		case reflect.Int:
			val := value.Field(item.Index)
			// fmt.Println("doDefaultValue: ", val.Int())
			if val.Int() == 0 {
				err := mapping(item.Field, val, item.Val)
				if err != nil {
					panic(err)
				}
			}
		}
	}
}

func validRestFn(fnType reflect.Type) bool {
	// 必须是函数，必须有两个入参，必须有一个返回值
	if fnType.Kind() != reflect.Func || fnType.NumIn() != 2 || fnType.NumOut() != 1 {
		return false
	}

	// 第一个入参必须是 SpringWeb.WebContext 类型
	if fnType.In(0) != contextType {
		return false
	}

	req := fnType.In(1) // 第二个入参必须是结构体指针
	return req.Kind() == reflect.Ptr && req.Elem().Kind() == reflect.Struct
}

func validRestFnNew(fn interface{}) web.Handler {
	fnType := reflect.TypeOf(fn)
	// 必须是函数，必须有一个返回值
	if fnType.Kind() != reflect.Func || fnType.NumOut() != 1 || fnType.NumIn() > 3 {
		panic(errors.New("方法传参错误，请阅读文档，重新修改"))
	}

	webContextIndex := -1
	var requestPathParamValueType reflect.Type = nil
	var requestQueryParamValueType reflect.Type = nil
	var requestTokenParamValueType reflect.Type = nil
	var valueType reflect.Type = nil

	restBindDefaultValues := make([]*restHandleDefaultValue, 0)

	for i := 0; i < fnType.NumIn(); i++ {
		var requestPathParamValueTypeTemp reflect.Type = nil
		var requestTokenParamValueTypeTemp reflect.Type = nil
		var requestQueryParamValueTypeTemp reflect.Type = nil
		t := fnType.In(i)
		if t == contextType {
			webContextIndex = i
		} else {
			if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
				if requestPathParamValueType != nil {
					// 已经赋值过PathParam, 直接bindValue
					valueType = t
					continue
				}
				// 其他对象
				for i := 0; i < t.Elem().NumField(); i++ {
					field := t.Elem().Field(i)
					if field.Type == requestPathParamPtrType {
						requestPathParamValueType = t
						requestPathParamValueTypeTemp = t
						break
					} else if field.Type == requestTokenParamPtrType {
						requestTokenParamValueType = t
						requestTokenParamValueTypeTemp = t
						break
					} else if field.Type == requestQueryParamPtrType {
						requestQueryParamValueType = t
						requestQueryParamValueTypeTemp = t
						break
					}
				}
				if requestPathParamValueTypeTemp == nil && requestTokenParamValueTypeTemp == nil && requestQueryParamValueTypeTemp == nil {
					// 未找到对应的Param, 直接bindValue
					valueType = t
					for i := 0; i < t.Elem().NumField(); i++ {
						field := t.Elem().Field(i)
						// 判断是否设置了这个tag
						formTag := field.Tag.Get("default")
						if formTag != "" {
							restBindDefaultValues = append(restBindDefaultValues, &restHandleDefaultValue{
								Field: field,
								Index: i,
								Val:   formTag,
							})
						}
					}
				}
			} else {
				panic(errors.New("方法传参必须是结构体指针"))
			}
		}
	}

	return &restHandler{
		fn:                    fn,
		fnType:                fnType,
		fnValue:               reflect.ValueOf(fn),
		hasContext:            webContextIndex != -1,
		pathParamType:         requestPathParamValueType,
		queryParamType:        requestQueryParamValueType,
		tokenParamType:        requestTokenParamValueType,
		bindType:              valueType,
		restBindDefaultValues: restBindDefaultValues,
	}
}

// REST 转换成 REST 形式的 Web 处理接口
func REST(fn interface{}) web.Handler {
	return validRestFnNew(fn)
}

// REST 转换成 REST 形式的 Web 处理接口
func REST1(fn interface{}) web.Handler {
	if fnType := reflect.TypeOf(fn); validRestFn(fnType) {
		return &restHandler{
			fn:            fn,
			fnType:        fnType,
			fnValue:       reflect.ValueOf(fn),
			hasContext:    true,
			pathParamType: nil,
			bindType:      fnType.In(1),
		}
	}
	panic(errors.New("fn should be func(SpringWeb.WebContext, *struct})anything"))
}

// RpcInvoke 可自定义的 rpc 执行函数。
var RpcInvoke = func(webCtx web.Context, fn func(web.Context) interface{}) {
	webCtx.SetHeader("Content-Type", "application/json")

	defer func() {
		if r := recover(); r != nil {
			result, ok := r.(*RestError)
			if !ok {
				var err error
				if err, ok = r.(error); !ok {
					err = errors.New(fmt.Sprint(r))
				}
				result = NewRestError(http.StatusInternalServerError, "internal server error")
				log.Printf("internal server error: %s", err.Error())
				// panic(err)
			}
			webCtx.SetStatus(result.Status)
			webCtx.JSON(result)
		}
	}()

	result := fn(webCtx)
	webCtx.SetStatus(http.StatusOK)
	webCtx.JSON(result)
}
